#include "../include/global.h"
#include "../include/keyboard_text.h"
#include "../include/event_data.h"
#include "../include/event_object_movement.h"
#include "../include/event_scripts.h"
#include "../include/field_effect.h"
#include "../include/field_player_avatar.h"
#include "../include/field_specials.h"
#include "../include/graphics.h"
#include "../include/bg.h" // Ensure this header contains the definition of struct BgTemplate
#include "../include/menu.h"
#include "../include/overworld.h"
#include "../include/naming_screen.h"
#include "../include/new_menu_helpers.h"
#include "../include/pokemon_icon.h"
#include "../include/pokemon_storage_system.h"
#include "../include/new/pokemon_storage_system.h"
//#include "strings.h"
#include "../include/task.h"
#include "../include/text_window.h"
#include "../include/trig.h"
#include "../include/event_object_movement.h"
#include "../include/palette.h"
#include "../include/string_util.h"
#include "../include/sprite.h"
#include "../include/constants/songs.h"
#include "../include/constants/event_objects.h"
#include "../include/decompress.h"
#include "../include/new/Vanilla_functions.h"
#include "../include/gpu_regs.h"
#include "../include/malloc.h"

enum {
    INPUT_NONE,
    INPUT_DPAD_UP,
    INPUT_DPAD_DOWN,
    INPUT_DPAD_LEFT,
    INPUT_DPAD_RIGHT,
    INPUT_A_BUTTON,
    INPUT_B_BUTTON,
    INPUT_LR_BUTTON,
    INPUT_SELECT,
    INPUT_START, //OK->添加/删除常用字 跳转首字母
	INPUT_R_BUTTON, //中文界面向右翻页
	INPUT_L_BUTTON, //中文界面向左翻页
};

#define KBROW_COUNT 4
#define KBCOL_COUNT 12

enum {
    GFXTAG_BACK_BUTTON,
    GFXTAG_OK_BUTTON,
    GFXTAG_PAGE_SWAP_FRAME,
    GFXTAG_PAGE_SWAP_BUTTON,
    GFXTAG_PAGE_SWAP_UPPER,
    GFXTAG_PAGE_SWAP_LOWER,
    GFXTAG_PAGE_SWAP_OTHERS,
    GFXTAG_CURSOR,
    GFXTAG_CURSOR_SQUISHED,
    GFXTAG_CURSOR_FILLED,
    GFXTAG_INPUT_ARROW,
    GFXTAG_UNDERSCORE,
    GFXTAG_RIVAL = 255,
};

enum {
    PALTAG_PC_ICON, // Also the PC icon
    PALTAG_PAGE_SWAP_UPPER,
    PALTAG_PAGE_SWAP_LOWER,
    PALTAG_PAGE_SWAP_OTHERS, // Also the input arrow/underscore
    PALTAG_PAGE_SWAP,
    PALTAG_CURSOR,
    PALTAG_BACK_BUTTON,
    PALTAG_OK_BUTTON,
    PALTAG_RIVAL = 255,
};

// The constants for the pages are needlessly complicated because GF didn't keep the indexing order consistent
// This set is used for sNamingScreen->currentPage. It uses the order that the pages are cycled in
enum {
    KBPAGE_LETTERS_CH,
    KBPAGE_LETTERS_LOWER,
    KBPAGE_LETTERS_UPPER,
    KBPAGE_SYMBOLS,
    KBPAGE_COUNT,
};

// This set is used for initializing a page's keyboard text and getting its number of columns
enum {
    KEYBOARD_LETTERS_LOWER,
    KEYBOARD_LETTERS_UPPER,
    KEYBOARD_SYMBOLS,
    KEYBOARD_CH,
};

enum {
    KEY_ROLE_CHAR,
    KEY_ROLE_PAGE,
    KEY_ROLE_BACKSPACE,
    KEY_ROLE_OK,
};

enum {
    BUTTON_PAGE,
    BUTTON_BACK,
    BUTTON_OK,
    BUTTON_COUNT,
};

// states for Task_NamingScreen
enum {
    STATE_FADE_IN,
    STATE_WAIT_FADE_IN,
    STATE_HANDLE_INPUT,
    STATE_MOVE_TO_OK_BUTTON,
    STATE_START_PAGE_SWAP,
    STATE_WAIT_PAGE_SWAP,
    STATE_PRESSED_OK,
    STATE_WAIT_SENT_TO_PC_MESSAGE,
    STATE_FADE_OUT,
    STATE_EXIT,
};

EWRAM_DATA struct NamingScreenData * sNamingScreen = NULL;

extern u16 gKeyRepeatStartDelay;

// extern text
extern const u8 gText_YourName[];
extern const u8 gText_BoxName[];
extern const u8 gText_PkmnsNickname[];


// start of .rodata

extern const struct SubspriteTable sSubspriteTable_PCIcon[];
extern const struct SpriteTemplate sSpriteTemplate_Cursor;
extern const struct SpriteTemplate sSpriteTemplate_InputArrow;
extern const struct SpriteTemplate sSpriteTemplate_Underscore;
extern const struct SpriteTemplate sSpriteTemplate_PCIcon;
const struct SpriteSheet sSpriteSheets[];
const struct SpritePalette sSpritePalettes[];
const struct NamingScreenTemplate *const sNamingScreenTemplates[];

const u16 sPCIconOff_Gfx[] = INCBIN_U16("graphics/naming_screen/pc_icon_off.4bpp");
const u16 sPCIconOn_Gfx[] = INCBIN_U16("graphics/naming_screen/pc_icon_on.4bpp");
const u16 sRival_Gfx[] = INCBIN_U16("graphics/naming_screen/rival.4bpp");
const u16 sKeyboard_Pal[] = INCBIN_U16("graphics/naming_screen/keyboard.gbapal");

const ALIGNED(1) u8 sKeyboardTextColors[6] =
{
     TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GREY,
     TEXT_COLOR_TRANSPARENT, 14, 15
};

extern const u8 *const sTransferredToPCMessages[4];

const struct BgTemplate sBgTemplates[4] =
{
    {
        .bg = 0,
        .charBaseIndex = 0,
        .mapBaseIndex = 30,
        .priority = 0,
    },{
        .bg = 1,
        .charBaseIndex = 2,
        .mapBaseIndex = 29,
        .priority = 1,
    }, {
        .bg = 2,
        .charBaseIndex = 2,
        .mapBaseIndex = 28,
        .priority = 2,
    }, {
        .bg = 3,
        .charBaseIndex = 3,
        .mapBaseIndex = 31,
        .priority = 3,
    }
};

const struct WindowTemplate sWindowTemplates[WIN_COUNT + 1] =
{
    [WIN_KB_PAGE_1] = {
        .bg = 1,
        .tilemapLeft = 3,
        .tilemapTop = 10,
        .width = 19,
        .height = 8,
        .paletteNum = 10,
        .baseBlock = 0x0030
    },
    [WIN_KB_PAGE_2] = {
        .bg = 2,
        .tilemapLeft = 3,
        .tilemapTop = 9,
        .width = 22,
        .height = 8,
        .paletteNum = 10,
        .baseBlock = 0x00c8
    },
    [WIN_TEXT_ENTRY] = {
        .bg = 2,
        .tilemapLeft = 12,
        .tilemapTop = 2,
        .width = 14,
        .height = 2,
        .paletteNum = 10,
        .baseBlock = 0x0030
    },
    [WIN_TEXT_ENTRY_BOX] = {
        .bg = 2,
        .tilemapLeft = 9,
        .tilemapTop = 4,
        .width = 16,
        .height = 2,
        .paletteNum = 10,
        .baseBlock = 0x004c
    },
    [WIN_BANNER] = {
        .bg = 0,
        .tilemapLeft = 2,
        .tilemapTop = 17,
        .width = 30,
        .height = 3,
        .paletteNum = 1,
        .baseBlock = 0x074
    },
    [WIN_CH] = {
        .bg = 2,
        .tilemapLeft = 2,
        .tilemapTop = 6,
        .width = 26,
        .height = 2,
        .paletteNum = 10,
        .baseBlock = 392
    },
    [WIN_PINYIN] = {
        .bg = 2,
        .tilemapLeft = 8,
        .tilemapTop = 0,
        .width = 6,
        .height = 2,
        .paletteNum = 10,
        .baseBlock = 450
    },
    DUMMY_WIN_TEMPLATE
};

const u8 gText_NamingScreenKeyboard_Upper0[] = _("QWERTYUIOP");
const u8 gText_NamingScreenKeyboard_Upper1[] = _("ASDFGHJKL");
const u8 gText_NamingScreenKeyboard_Upper2[] = _("ZXCVBNM");
const u8 gText_NamingScreenKeyboard_Lower0[] = _("qwertyuiop");
const u8 gText_NamingScreenKeyboard_Lower1[] = _("asdfghjkl");
const u8 gText_NamingScreenKeyboard_Lower2[] = _("zxcvbnm");
const u8 gText_NamingScreenKeyboard_Symbol0[] = _("0123456789");
const u8 gText_NamingScreenKeyboard_Symbol1[] = _("!?♂♀/+-{PKMN}");
const u8 gText_NamingScreenKeyboard_Symbol2[] = _("…“”‘'");


const u8* const gText_NamingScreenKeyboard_Words[][3] =
{
    [KEYBOARD_LETTERS_LOWER] =
    {
        gText_NamingScreenKeyboard_Lower0,gText_NamingScreenKeyboard_Lower1,gText_NamingScreenKeyboard_Lower2
    },
    [KEYBOARD_LETTERS_UPPER] =
    {
        gText_NamingScreenKeyboard_Upper0,gText_NamingScreenKeyboard_Upper1,gText_NamingScreenKeyboard_Upper2,
    },
    [KEYBOARD_SYMBOLS] =
    {
        gText_NamingScreenKeyboard_Symbol0,gText_NamingScreenKeyboard_Symbol1,gText_NamingScreenKeyboard_Symbol2
    },
    [KEYBOARD_CH] =
    {
        gText_NamingScreenKeyboard_Lower0,gText_NamingScreenKeyboard_Lower1,gText_NamingScreenKeyboard_Lower2
    },
};

const u8 sColumnCounts[KBPAGE_COUNT][KBROW_COUNT - 1] = {
    [KEYBOARD_LETTERS_LOWER] =
    {
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower0) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower1) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower2) - 1
    },
    [KEYBOARD_LETTERS_UPPER] =
    {
        ARRAY_COUNT(gText_NamingScreenKeyboard_Upper0) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Upper1) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Upper2) - 1
    },
    [KEYBOARD_SYMBOLS] =
    {
        ARRAY_COUNT(gText_NamingScreenKeyboard_Symbol0) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Symbol1) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Symbol2) - 1
    },
    [KEYBOARD_CH] =
    {
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower0) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower1) - 1,
        ARRAY_COUNT(gText_NamingScreenKeyboard_Lower2) - 1
    },
};

const u32 gNamingScreenBg_Gfx[] = INCBIN_U32("graphics/naming_screen/new/background.4bpp.lz");
const u32 gNamingScreenBg_Tilemap[] = INCBIN_U32("graphics/naming_screen/new/background.bin.lz");
const u32 gNamingScreenBg_Pal[] = INCBIN_U32("graphics/naming_screen/new/background.gbapal");
//实际大小23*17
const u32 gNamingScreenBg_Rect[] = INCBIN_U32("graphics/naming_screen/new/rect.4bpp.lz");
const u32 gNamingScreenBg_Rect2[] = INCBIN_U32("graphics/naming_screen/new/rect2.4bpp.lz");
// forward declarations

void CB2_LoadNamingScreen(void)
{
    switch (gMain.state)
    {
    case 0:
        ResetVHBlank();
        NamingScreen_Init();
        gMain.state++;
        break;
    case 1:
        NamingScreen_InitBGs();
        gMain.state++;
        break;
    case 2:
        ResetPaletteFade();
        gMain.state++;
        break;
    case 3:
        ResetSpriteData();
        FreeAllSpritePalettes();
        gMain.state++;
        break;
    case 4:
        ResetTasks();
        gMain.state++;
        break;
    case 5:
        LoadPalettes();
        gMain.state++;
        break;
    case 6:
        LoadGfx();
        gMain.state++;
        break;
    case 7:
        CreateSprites();
        UpdatePaletteFade();
        NamingScreen_ShowBgs();
        gMain.state++;
        break;
    default:
        CreateHelperTasks();
        CreateNamingScreenTask();
        break;
    }
}

void NamingScreen_Init(void)
{
    sNamingScreen->state = STATE_FADE_IN;
    sNamingScreen->bg1vOffset = 0;
    sNamingScreen->bg2vOffset = 0;
    sNamingScreen->bg1Priority = BGCNT_PRIORITY(1);
    sNamingScreen->bg2Priority = BGCNT_PRIORITY(2);
    sNamingScreen->bgToReveal = 0;
    sNamingScreen->bgToHide = 1;
    sNamingScreen->template = sNamingScreenTemplates[sNamingScreen->templateNum];
    sNamingScreen->currentPage = sNamingScreen->template->initialPage;
    sNamingScreen->inputCharBaseXPos = (240 - sNamingScreen->template->maxChars * 8) / 2 + 16;
    memset(sNamingScreen->textBuffer, EOS, sizeof(sNamingScreen->textBuffer));
    if (sNamingScreen->template->copyExistingString)
        *StringCopy(sNamingScreen->textBuffer, sNamingScreen->destBuffer);
    gKeyRepeatStartDelay = 16;
    sNamingScreen->curChRowMax = 0;
    sNamingScreen->chBufferCount = 0;
    sNamingScreen->curChChars = NULL;
    sNamingScreen->keyRole = KEY_ROLE_CHAR;
}

void SetSpritesVisible(void)
{
    u8 i;
    for (i = 0; i < MAX_SPRITES; i++)
    {
        if (gSprites[i].inUse)
            gSprites[i].invisible = FALSE;
    }
    SetCursorInvisibility(0);
}

void NamingScreen_InitBGs(void)
{
    u8 i;

    DmaClearLarge16(3, (void *)VRAM, VRAM_SIZE, 0x1000);
    DmaClear32(3, (void *)OAM, OAM_SIZE);
    DmaClear16(3, (void *)PLTT, PLTT_SIZE);

    SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0);
    ResetBgsAndClearDma3BusyFlags(FALSE);
    InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));

    ChangeBgX(0, 0, 0);
    ChangeBgY(0, 0, 0);
    ChangeBgX(1, 0, 0);
    ChangeBgY(1, 0, 0);
    ChangeBgX(2, 0, 0);
    ChangeBgY(2, 0, 0);
    ChangeBgX(3, 0, 0);
    ChangeBgY(3, 0, 0);

    InitStandardTextBoxWindows();
    InitTextBoxGfxAndPrinters();

    for (i = 0; i < WIN_COUNT; i++)
        sNamingScreen->windows[i] = AddWindow(&sWindowTemplates[i]);

    SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON);
    SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2);
    SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(12, 8));

    SetBgTilemapBuffer(1, sNamingScreen->tilemapBuffer1);
    SetBgTilemapBuffer(2, sNamingScreen->tilemapBuffer2);
    SetBgTilemapBuffer(3, sNamingScreen->tilemapBuffer3);

    FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 0x20, 0x20);
    FillBgTilemapBufferRect_Palette0(2, 0, 0, 0, 0x20, 0x20);
    FillBgTilemapBufferRect_Palette0(3, 0, 0, 0, 0x20, 0x20);
}

void CreateNamingScreenTask(void)
{
    CreateTask(Task_NamingScreen, 2);
    SetMainCallback2(CB2_NamingScreen);
}

bool8 MainState_StartPageSwap(void)
{
    SetInputState(INPUT_STATE_DISABLED);
    SetCursorInvisibility(TRUE);
    TryStartButtonFlash(BUTTON_PAGE, FALSE, TRUE);
    PrintKeyboardKeys(sNamingScreen->windows[WIN_KB_PAGE_2], CurrentPageToNextKeyboardId());
    PlaySE(SE_WIN_OPEN);
    sNamingScreen->state = STATE_WAIT_PAGE_SWAP;
    return FALSE;
}

void Task_NamingScreen(u8 taskId)
{
    switch (sNamingScreen->state)
    {
    case STATE_FADE_IN:
        MainState_FadeIn();
        SetSpritesVisible();
        SetVBlank();
        break;
    case STATE_WAIT_FADE_IN:
        MainState_WaitFadeIn();
        break;
    case STATE_HANDLE_INPUT:
        MainState_HandleInput();
        break;
    case STATE_MOVE_TO_OK_BUTTON:
        MainState_MoveToOKButton();
        MainState_HandleInput();
        break;
    case STATE_START_PAGE_SWAP:
        MainState_StartPageSwap();
        break;
    case STATE_WAIT_PAGE_SWAP:
        MainState_WaitPageSwap();
        break;
    case STATE_PRESSED_OK:
        MainState_PressedOKButton();
        break;
    case STATE_WAIT_SENT_TO_PC_MESSAGE:
        MainState_WaitSentToPCMessage();
        break;
    case STATE_FADE_OUT:
        MainState_FadeOut();
        break;
    case STATE_EXIT:
        MainState_Exit();
        break;
    }
}

const u8 sPageToNextKeyboardId[KBPAGE_COUNT] =
{
    [KBPAGE_LETTERS_CH]    = KEYBOARD_LETTERS_LOWER,
    [KBPAGE_LETTERS_LOWER] = KEYBOARD_LETTERS_UPPER,
    [KBPAGE_LETTERS_UPPER] = KEYBOARD_SYMBOLS,
    [KBPAGE_SYMBOLS]       = KEYBOARD_CH,
};

const u8 sPageToKeyboardId[KBPAGE_COUNT] = 
{ 
    [KBPAGE_SYMBOLS]       = KEYBOARD_SYMBOLS, 
    [KBPAGE_LETTERS_UPPER] = KEYBOARD_LETTERS_UPPER, 
    [KBPAGE_LETTERS_LOWER] = KEYBOARD_LETTERS_LOWER,
    [KBPAGE_LETTERS_CH] =   KEYBOARD_CH,
};

u8 CurrentPageToKeyboardId(void)
{
    return sPageToKeyboardId[sNamingScreen->currentPage];
}

bool8 MainState_FadeIn(void)
{
    DecompressToBgTilemapBuffer(3, gNamingScreenBg_Tilemap);
    sNamingScreen->currentPage = KBPAGE_LETTERS_CH;
    PrintKeyboardKeys(sNamingScreen->windows[WIN_KB_PAGE_2], CurrentPageToKeyboardId());
    DrawTextEntry();
    DrawCotrolHightLightByPage(KBPAGE_LETTERS_CH);
    CopyBgTilemapBufferToVram(1);
    CopyBgTilemapBufferToVram(2);
    CopyBgTilemapBufferToVram(3);
    BlendPalettes(-1, 16, 0);
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB_BLACK);
    sNamingScreen->state++;
    return FALSE;
}

bool8 MainState_WaitFadeIn(void)
{
    if (!gPaletteFade.active)
    {
        SetInputState(INPUT_STATE_ENABLED);
        SetCursorFlashing(TRUE);
        sNamingScreen->state++;
    }
    return FALSE;
}

bool8 MainState_HandleInput(void)
{
    return HandleKeyboardEvent();
}

bool8 MainState_MoveToOKButton(void)
{
    if (IsCursorAnimFinished())
    {
        SetInputState(INPUT_STATE_ENABLED);
        MoveCursorToOKButton();
        sNamingScreen->state = STATE_HANDLE_INPUT;
    }
    return FALSE;
}

bool8 MainState_PressedOKButton(void)
{
    SaveInputText();
    SetInputState(INPUT_STATE_DISABLED);
    SetCursorFlashing(FALSE);
    TryStartButtonFlash(BUTTON_COUNT, FALSE, TRUE);
    if (sNamingScreen->templateNum == NAMING_SCREEN_CAUGHT_MON
        && CalculatePlayerPartyCount() >= PARTY_SIZE)
    {
        DisplaySentToPCMessage();
        sNamingScreen->state = STATE_WAIT_SENT_TO_PC_MESSAGE;
        return FALSE;
    }
    else
    {
        sNamingScreen->state = STATE_FADE_OUT;
        return TRUE;  //Exit the naming screen
    }
}

bool8 MainState_FadeOut(void)
{
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK);
    sNamingScreen->state++;
    return FALSE;
}

bool8 MainState_Exit(void)
{
    if (!gPaletteFade.active)
    {
        if (sNamingScreen->templateNum == NAMING_SCREEN_PLAYER)
            SeedRngAndSetTrainerId();
        SetMainCallback2(sNamingScreen->returnCallback);
        DestroyTask(FindTaskIdByFunc(Task_NamingScreen));
        FreeAllWindowBuffers();
        FREE_AND_SET_NULL(sNamingScreen);
    }
    return FALSE;
}

typedef u8 BoxNameT[9];

#define ORIGINAL_BOX_NAME_RAM ((BoxNameT*) (0x2029314 + 0x8344))

BoxNameT* const sPokemonBoxNamePtrs[TOTAL_BOXES_COUNT] =
{
	ORIGINAL_BOX_NAME_RAM,		//Box 1
	ORIGINAL_BOX_NAME_RAM + 1,	//Box 2
	ORIGINAL_BOX_NAME_RAM + 2,	//Box 3
	ORIGINAL_BOX_NAME_RAM + 3,	//Box 4
	ORIGINAL_BOX_NAME_RAM + 4,	//Box 5
	ORIGINAL_BOX_NAME_RAM + 5,	//Box 6
	ORIGINAL_BOX_NAME_RAM + 6,	//Box 7
	ORIGINAL_BOX_NAME_RAM + 7,	//Box 8
	ORIGINAL_BOX_NAME_RAM + 8,	//Box 9
	ORIGINAL_BOX_NAME_RAM + 9,	//Box 10
	ORIGINAL_BOX_NAME_RAM + 10,	//Box 11
	ORIGINAL_BOX_NAME_RAM + 11,	//Box 12
	ORIGINAL_BOX_NAME_RAM + 12,	//Box 13
	ORIGINAL_BOX_NAME_RAM + 13,	//Box 14
	ORIGINAL_BOX_NAME_RAM - 1,	//Box 15
	ORIGINAL_BOX_NAME_RAM - 2,	//Box 16
	ORIGINAL_BOX_NAME_RAM - 3,	//Box 17
	ORIGINAL_BOX_NAME_RAM - 4,	//Box 18
	ORIGINAL_BOX_NAME_RAM - 5,	//Box 19
	ORIGINAL_BOX_NAME_RAM - 6,	//Box 20
	ORIGINAL_BOX_NAME_RAM - 7,	//Box 21
	ORIGINAL_BOX_NAME_RAM - 8,	//Box 22
	ORIGINAL_BOX_NAME_RAM - 9,	//Box 23
	ORIGINAL_BOX_NAME_RAM - 10,	//Box 24
	ORIGINAL_BOX_NAME_RAM - 11,	//Box 25
};

u8* GetBoxNamePtr(u8 boxId)
{
	if (boxId < TOTAL_BOXES_COUNT)
		return (u8*) sPokemonBoxNamePtrs[boxId];
	else
		return NULL;
}

void DisplaySentToPCMessage(void)
{
    u8 stringToDisplay = 0;

    if (!IsDestinationBoxFull())
    {
        StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_PC_BOX_TO_SEND_MON)));
        StringCopy(gStringVar2, sNamingScreen->destBuffer);
    }
    else
    {
        StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_PC_BOX_TO_SEND_MON)));
        StringCopy(gStringVar2, sNamingScreen->destBuffer);
        StringCopy(gStringVar3, GetBoxNamePtr(GetPCBoxToSendMon()));
        stringToDisplay = 2;
    }

    if (FlagGet(FLAG_SYS_NOT_SOMEONES_PC))
        stringToDisplay++;

    StringExpandPlaceholders(gStringVar4, sTransferredToPCMessages[stringToDisplay]);
    DrawDialogueFrame(0, 0);
    gTextFlags.canABSpeedUpPrint = TRUE;
    AddTextPrinterParameterized2(0, 1, gStringVar4, 1, 0, 2, 1, 3);
    CopyWindowToVram(0, 3);
}

const u8 gHightLightConfig[][2] =
{
    [KBPAGE_LETTERS_CH] = {6, 32},
    [KBPAGE_LETTERS_LOWER] = {30, 32},
    [KBPAGE_LETTERS_UPPER] = {30, 32},
    [KBPAGE_SYMBOLS] = {54, 32},
};

void DrawCotrolHightLight(u8 x, u8 width, u8 type, u8 page)
{
    FillWindowPixelBuffer(sNamingScreen->windows[WIN_BANNER], PIXEL_FILL(0));
    BlitBitmapToWindow(sNamingScreen->windows[WIN_BANNER], type == 0?sNamingScreen->rectBuffer:sNamingScreen->rectBuffer2, x, 5, width, 32);
    PrintControls(page);
}

void DrawCotrolHightLightByPage(u8 page)
{
    DrawCotrolHightLight(gHightLightConfig[page][0], gHightLightConfig[page][1], 0, page);
}

bool8 MainState_WaitSentToPCMessage(void)
{
    RunTextPrinters();
    if (!IsTextPrinterActive(0) && JOY_NEW(A_BUTTON))
        sNamingScreen->state = STATE_FADE_OUT;

    return FALSE;
}

bool8 MainState_WaitPageSwap(void)
{
    s16 cursorX;
    s16 cursorY;
    u8 column;

    if (IsPageSwapAnimNotInProgress())
    {

        GetCursorPos(&cursorX, &cursorY);
        column = GetCurrentPageColumnCountByRow(cursorY);
        
         if (cursorX >= column)
             cursorX = column - 1;
        if (IsPageChMode() && cursorY == 0)
        {
            cursorY = 1;
            cursorX = 0;
        }

        SetCursorPos(cursorX, cursorY);
        sNamingScreen->state = STATE_HANDLE_INPUT;
        sNamingScreen->currentPage++;
        sNamingScreen->currentPage %= KBPAGE_COUNT;
        SetInputState(INPUT_STATE_ENABLED);
        SetCursorInvisibility(FALSE);
        ClearChiniesTextEntry();
        DrawCotrolHightLightByPage(sNamingScreen->currentPage);
    }
    return FALSE;
}

//--------------------------------------------------
// Page Swap
//--------------------------------------------------

#define tState data[0]
#define tFrameCount data[1]

bool8 (*const sPageSwapAnimStateFuncs[])(struct Task *task) =
{
    PageSwapAnimState_Init,
    PageSwapAnimState_1,
    PageSwapAnimState_2,
    PageSwapAnimState_Done
};

void Task_HandlePageSwapAnim(u8 taskId)
{
    while (sPageSwapAnimStateFuncs[gTasks[taskId].tState](&gTasks[taskId]))
        ;
}

bool8 IsPageSwapAnimNotInProgress(void)
{
    if (FindTaskIdByFunc(Task_HandlePageSwapAnim) == 0xFF)
        return TRUE;
    else
        return FALSE;
}

bool8 PageSwapAnimState_Init(struct Task *task)
{
    sNamingScreen->bg1vOffset = 0;
    sNamingScreen->bg2vOffset = 0;
    task->tState++;
    return 0;
}

bool8 PageSwapAnimState_Done(struct Task *task)
{
    DestroyTask(FindTaskIdByFunc(Task_HandlePageSwapAnim));
    return 0;
}

#undef tState
#undef tFrameCount

//--------------------------------------------------
// Cursor blink
//--------------------------------------------------

#define tButtonId     data[0]
#define tKeepFlashing data[1]
#define tAllowFlash   data[2]
#define tColor        data[3]
#define tColorIncr    data[4]
#define tColorDelay   data[5]
#define tColorDelta   data[6]

void CreateButtonFlashTask(void)
{
    u8 taskId = CreateTask(Task_UpdateButtonFlash, 3);
    gTasks[taskId].tButtonId = BUTTON_COUNT;
}

void TryStartButtonFlash(u8 button, bool8 keepFlashing, bool8 interruptCurFlash)
{
}

void Task_UpdateButtonFlash(u8 taskId)
{
    struct Task *task = &gTasks[taskId];

    if (task->tButtonId == BUTTON_COUNT || !task->tAllowFlash)
        return;

    MultiplyInvertedPaletteRGBComponents(GetButtonPalOffset(task->tButtonId), task->tColor, task->tColor, task->tColor);

    if (task->tColorDelay && --task->tColorDelay)
        return;

    task->tColorDelay = 2;
    if (task->tColorIncr >= 0)
    {
        if (task->tColor < 14)
        {
            task->tColor += task->tColorIncr;
            task->tColorDelta += task->tColorIncr;
        }
        else
        {
            task->tColor = 16;
            task->tColorDelta++;
        }
    }
    else
    {
        task->tColor += task->tColorIncr;
        task->tColorDelta += task->tColorIncr;
    }

    if (task->tColor == 16 && task->tColorDelta == 22)
    {
        task->tColorIncr = -4;
    }
    else if (task->tColor == 0)
    {
        task->tAllowFlash = task->tKeepFlashing;
        task->tColorIncr = 2;
        task->tColorDelta = 0;
    }
}

u16 GetButtonPalOffset(u8 button)
{
    const u16 palOffsets[BUTTON_COUNT + 1] =
    {
        [BUTTON_PAGE]  = IndexOfSpritePaletteTag(PALTAG_PAGE_SWAP) * 16 + 0x10E,
        [BUTTON_BACK]  = IndexOfSpritePaletteTag(PALTAG_BACK_BUTTON) * 16 + 0x10E,
        [BUTTON_OK]    = IndexOfSpritePaletteTag(PALTAG_OK_BUTTON) * 16 + 0x10E,
        [BUTTON_COUNT] = IndexOfSpritePaletteTag(PALTAG_OK_BUTTON) * 16 + 0x101,
    };

    return palOffsets[button];
}

#undef tButtonId
#undef tKeepFlashing
#undef tAllowFlash
#undef tColor
#undef tColorIncr
#undef tColorDelay
#undef tColorDelta

//--------------------------------------------------
// Cursor
//--------------------------------------------------

// Sprite data for the the cursor
#define sX          data[0]
#define sY          data[1]
#define sPrevX      data[2]
#define sPrevY      data[3]
#define sInvisible  data[4] & 0x00FF
#define sFlashing   data[4] & 0xFF00
#define sColor      data[5]
#define sColorIncr  data[6]
#define sColorDelay data[7]

void SpriteCB_Cursor(struct Sprite *sprite)
{
    if (sprite->animEnded)
        StartSpriteAnim(sprite, 0);

    // Hide cursor when on button column
    sprite->invisible = sprite->sInvisible;
    if (sprite->sX == GetCurrentPageColumnCountByRow(sprite->sY))
        sprite->invisible = TRUE;

    if (sprite->invisible
        || !(sprite->sFlashing))
    {
        sprite->sColor = 0;
        sprite->sColorIncr = 2;
        sprite->sColorDelay = 2;
    }

    sprite->sColorDelay--;
    if (sprite->sColorDelay == 0)
    {
        sprite->sColor += sprite->sColorIncr;
        if (sprite->sColor == 16 || sprite->sColor == 0)
            sprite->sColorIncr = -sprite->sColorIncr;
        sprite->sColorDelay = 2;
    }

    if (sprite->sFlashing)
    {
        s8 gb = sprite->sColor;
        s8 r = sprite->sColor >> 1;
        u16 index = IndexOfSpritePaletteTag(PALTAG_CURSOR) * 16 + 0x0101;

        MultiplyInvertedPaletteRGBComponents(index, r, gb, gb);
    }
}

#define sDelay  data[0]
#define sXPosId data[1]

void SpriteCB_InputArrow(struct Sprite *sprite)
{
    const s16 x[] = {0, -4, -2, -1};

    if (sprite->sDelay == 0 || --sprite->sDelay == 0)
    {
        sprite->sDelay = 8;
        sprite->sXPosId = (sprite->sXPosId + 1) & (ARRAY_COUNT(x) - 1);
    }
    sprite->x2 = x[sprite->sXPosId];
}

#undef sDelay
#undef sXPosId

#define sId     data[0] // set in CreateTextEntrySprites
#define sYPosId data[1]
#define sDelay  data[2]

void SpriteCB_Underscore(struct Sprite *sprite)
{
    const s16 y[] = {2, 3, 2, 1};
    u8 pos = GetTextEntryPosition();

    if (pos != (u8)sprite->sId)
    {
        sprite->y2 = 0;
        sprite->sYPosId = 0;
        sprite->sDelay = 0;
    }
    else
    {
        sprite->y2 = y[sprite->sYPosId];
        sprite->sDelay++;
        if (sprite->sDelay > 8)
        {
            sprite->sYPosId = (sprite->sYPosId + 1) & (ARRAY_COUNT(y) - 1);
            sprite->sDelay = 0;
        }
    }
}

#undef sId
#undef sYPosId
#undef sDelay

void CreateSprites(void)
{
    CreateCursorSprite();
    CreateTextEntrySprites();
    CreateInputTargetIcon();
}

void CreateCursorSprite(void)
{
    sNamingScreen->cursorSpriteId = CreateSprite(&sSpriteTemplate_Cursor, 38, 88, 1);
    SetCursorInvisibility(TRUE);
    gSprites[sNamingScreen->cursorSpriteId].oam.priority = 1;
    gSprites[sNamingScreen->cursorSpriteId].oam.objMode = ST_OAM_OBJ_BLEND;
    gSprites[sNamingScreen->cursorSpriteId].sColorIncr = 1; // ? immediately overwritten
    gSprites[sNamingScreen->cursorSpriteId].sColorIncr = 2;
    SetCursorPos(0, 1);
}
#define ROW_HEIGHT 24
#define COLOUM_PAD 11

void SetCursorPos(s16 x, s16 y)
{
    struct Sprite *cursorSprite = &gSprites[sNamingScreen->cursorSpriteId];

    if (x < GetCurrentPageColumnCountByRow(y))
        cursorSprite->x = x * (COLOUM_PAD + 6) + 26;
    else
        cursorSprite->x = 0;

    cursorSprite->y = y * ROW_HEIGHT + 55;
    cursorSprite->sPrevX = cursorSprite->sX;
    cursorSprite->sPrevY = cursorSprite->sY;
    cursorSprite->sX = x;
    cursorSprite->sY = y;
}

void GetCursorPos(s16 *x, s16 *y)
{
    struct Sprite *cursorSprite = &gSprites[sNamingScreen->cursorSpriteId];

    *x = cursorSprite->sX;
    *y = cursorSprite->sY;
}

void MoveCursorToOKButton(void)
{
    DrawCotrolHightLight(145, 64, 1, sNamingScreen->currentPage);
    sNamingScreen->keyRole = KEY_ROLE_OK;
}

void SetCursorFlashing(bool8 flashing)
{
    gSprites[sNamingScreen->cursorSpriteId].data[4] &= 0xFF;
    gSprites[sNamingScreen->cursorSpriteId].data[4] |= flashing << 8; // sFlashing
}

void SquishCursor(void)
{
    StartSpriteAnim(&gSprites[sNamingScreen->cursorSpriteId], 1);
}

bool8 IsCursorAnimFinished(void)
{
    return gSprites[sNamingScreen->cursorSpriteId].animEnded;
}

u8 GetKeyRoleAtCursorPos(void)
{
    return sNamingScreen->keyRole;
}

// If the cursor's x is equal to the column count, cursor is in the button column
u8 GetCurrentPageColumnCountByRow(s16 y)
{
    if (y == 0)
        return sNamingScreen->curChRowMax;
    return sColumnCounts[CurrentPageToKeyboardId()][y - 1];
}

#undef sX
#undef sY
#undef sPrevX
#undef sPrevY
#undef sInvisible
#undef sFlashing
#undef sColor
#undef sColorIncr
#undef sColorDelay

#define sState          data[0]
#define sPage           data[1]
#define sTextSpriteId   data[6]
#define sButtonSpriteId data[7]

#undef sState
#undef sPage
#undef sTextSpriteId
#undef sButtonSpriteId

void CreateTextEntrySprites(void)
{
    u8 spriteId;
    s16 xPos;
    u8 i;

    const u8 arrowY= 26;
    const u8 textY = 30;
    xPos = sNamingScreen->inputCharBaseXPos - 5;
    spriteId = CreateSprite(&sSpriteTemplate_InputArrow, xPos, arrowY, 0);
    gSprites[spriteId].oam.priority = 3;
    gSprites[spriteId].invisible = TRUE;
    xPos = sNamingScreen->inputCharBaseXPos;
    for (i = 0; i < sNamingScreen->template->maxChars; i++, xPos += 8)
    {
        spriteId = CreateSprite(&sSpriteTemplate_Underscore, xPos + 3, textY, 0);
        gSprites[spriteId].oam.priority = 3;
        gSprites[spriteId].data[0] = i;
        gSprites[spriteId].invisible = TRUE;
    }
}

//--------------------------------------------------
// Icon creation (the thing you're naming or giving input to)
//--------------------------------------------------

void (*const sIconFunctions[])(void) =
{
    NamingScreen_NoIcon,
    NamingScreen_CreatePlayerIcon,
    NamingScreen_CreatePCIcon,
    NamingScreen_CreateMonIcon,
    NamingScreen_CreateRivalIcon
};

void CreateInputTargetIcon(void)
{
    sIconFunctions[sNamingScreen->template->iconFunction]();
}

void NamingScreen_NoIcon(void)
{

}

#define POS_ICON_X 26
#define POS_ICON_Y 22
void NamingScreen_CreatePlayerIcon(void)
{
    u8 rivalGfxId;
    u8 spriteId;

    rivalGfxId = GetRivalAvatarGraphicsIdByStateIdAndGender(0, sNamingScreen->monSpecies);
    spriteId = CreateObjectGraphicsSprite(rivalGfxId, SpriteCallbackDummy, POS_ICON_X, POS_ICON_Y - 3, 0);
    gSprites[spriteId].oam.priority = 3;
    StartSpriteAnim(&gSprites[spriteId], 4);
}

void NamingScreen_CreatePCIcon(void)
{
    u8 spriteId;
    spriteId = CreateSprite(&sSpriteTemplate_PCIcon, POS_ICON_X, POS_ICON_Y + 1, 0);
    SetSubspriteTables(&gSprites[spriteId], sSubspriteTable_PCIcon);
    gSprites[spriteId].oam.priority = 3;
}

void NamingScreen_CreateMonIcon(void)
{
    u8 spriteId;

    LoadMonIconPalettes();
    spriteId = CreateMonIcon(sNamingScreen->monSpecies, SpriteCallbackDummy,POS_ICON_X, POS_ICON_Y, 0,sNamingScreen->monPersonality, 1);
    gSprites[spriteId].oam.priority = 3;
}

const union AnimCmd sAnim_Rival[] =
{
    ANIMCMD_FRAME( 0, 10),
    ANIMCMD_FRAME(24, 10),
    ANIMCMD_FRAME( 0, 10),
    ANIMCMD_FRAME(32, 10),
    ANIMCMD_JUMP(0)
};

const union AnimCmd *const sAnims_Rival[] =
{
    sAnim_Rival
};

extern const u16 gNamingScreenRival_Pal[];

void NamingScreen_CreateRivalIcon(void)
{
    const struct SpriteSheet sheet = {
        sRival_Gfx, 0x900, GFXTAG_RIVAL
    };
    const struct SpritePalette palette = {
        gNamingScreenRival_Pal, PALTAG_RIVAL
    };
    struct SpriteTemplate template;
    const struct SubspriteTable * tables_p;
    u8 spriteId;

    CopyObjectGraphicsInfoToSpriteTemplate(0, SpriteCallbackDummy, &template, &tables_p);

    template.tileTag = sheet.tag;
    template.paletteTag = palette.tag;
    template.anims = sAnims_Rival;
    LoadSpriteSheet(&sheet);
    LoadSpritePalette(&palette);
    spriteId = CreateSprite(&template, POS_ICON_X, POS_ICON_Y - 3, 0);
    gSprites[spriteId].oam.priority = 3;
}

bool8 (*const sKeyboardKeyHandlers[])(u8) =
{
    [KEY_ROLE_CHAR]      = KeyboardKeyHandler_Character,
    [KEY_ROLE_PAGE]      = KeyboardKeyHandler_Page,
    [KEY_ROLE_BACKSPACE] = KeyboardKeyHandler_Backspace,
    [KEY_ROLE_OK]        = KeyboardKeyHandler_OK,
};

bool8 Array16Contains(const u16 *array, u16 size, u16 ch)
{
    u16 i;
    for (i = 0; i < size; i++) {
        if (array[i] == ch) {
            return TRUE;
        }
    }
    return FALSE;
}

s32 BinarySearch(const u16 array[], u16 size, u16 key)
{
    u32 left = 0, right = size - 1;
    while (left <= right) {
        u32 mid = (right + left) / 2;
        if (array[mid] > key) {
            right = mid - 1;
        } else if (array[mid] < key) {
            left = mid + 1;
        } else {
            return mid;
        }
    }
    return -1;
}

bool8 HandleKeyboardEvent(void)
{
    u8 event = GetInputEvent();
    u8 keyRole = GetKeyRoleAtCursorPos();
    bool8 ischinesepage = IsPageChMode();

    if (INPUT_B_BUTTON == event)
	{
		DeleteTextCharacter();
        return FALSE;
	}
	else if (INPUT_SELECT == event)
		return SwapKeyboardPage();
	else if (INPUT_R_BUTTON == event && ischinesepage) //翻到下一页
	{
		TurnChinesePage(TRUE);
		return FALSE;
	}
	else if (INPUT_L_BUTTON == event && ischinesepage) //翻到上一页
	{
        TurnChinesePage(FALSE);
		return FALSE;
	}
	else if (INPUT_START == event)
	{
        MoveCursorToOKButton();
		return FALSE;
	}
	else
		return sKeyboardKeyHandlers[keyRole](event);
}

bool8 KeyboardKeyHandler_Character(u8 input)
{
    TryStartButtonFlash(BUTTON_COUNT, FALSE, FALSE);
    if (input == INPUT_A_BUTTON)
    {
        bool8 textFull = AddTextCharacter();

        SquishCursor();
        if (textFull)
        {
            SetInputState(INPUT_STATE_DISABLED);
            sNamingScreen->state = STATE_MOVE_TO_OK_BUTTON;
        }
    }
    return FALSE;
}

bool8 KeyboardKeyHandler_Page(u8 input)
{
    TryStartButtonFlash(BUTTON_PAGE, TRUE, FALSE);
    if (input == INPUT_A_BUTTON)
        return SwapKeyboardPage();
    else
        return FALSE;
}

bool8 KeyboardKeyHandler_Backspace(u8 input)
{
    TryStartButtonFlash(BUTTON_BACK, TRUE, FALSE);
    if (input == INPUT_A_BUTTON)
        DeleteTextCharacter();
    return FALSE;
}

bool8 KeyboardKeyHandler_OK(u8 input)
{
    TryStartButtonFlash(BUTTON_OK, TRUE, FALSE);
    if (input == INPUT_A_BUTTON)
    {
        PlaySE(SE_SELECT);
        sNamingScreen->state = STATE_PRESSED_OK;
        return TRUE;
    }
    else
    {
        if (input != INPUT_NONE)
        {
            DrawCotrolHightLightByPage(sNamingScreen->currentPage);
            sNamingScreen->keyRole = KEY_ROLE_CHAR;
        }
        return FALSE;
    }
}

bool8 SwapKeyboardPage(void)
{
    sNamingScreen->state = STATE_START_PAGE_SWAP;
    return TRUE;
}

//--------------------------------------------------
// Input handling
//--------------------------------------------------

#define tState data[0]
#define tKeyboardEvent data[1]
#define tButtonId data[2]

void (*const sInputFuncs[])(struct Task *) =
{
    [INPUT_STATE_DISABLED] = Input_Disabled,
    [INPUT_STATE_ENABLED]  = Input_Enabled,
};

void CreateInputHandlerTask(void)
{
    CreateTask(Task_HandleInput, 1);
}

u8 GetInputEvent(void)
{
    u8 taskId = FindTaskIdByFunc(Task_HandleInput);

    return gTasks[taskId].tKeyboardEvent;
}

void SetInputState(u8 state)
{
    u8 taskId = FindTaskIdByFunc(Task_HandleInput);

    gTasks[taskId].tState = state;
}

void Task_HandleInput(u8 taskId)
{
    sInputFuncs[gTasks[taskId].tState](&gTasks[taskId]);
}

void Input_Disabled(struct Task *task)
{
    task->tKeyboardEvent = INPUT_NONE;
}

void Input_Enabled(struct Task *task)
{
    task->tKeyboardEvent = INPUT_NONE;

    if (JOY_NEW(A_BUTTON))
        task->tKeyboardEvent = INPUT_A_BUTTON;
    else if (JOY_NEW(B_BUTTON))
        task->tKeyboardEvent = INPUT_B_BUTTON;
    else if (JOY_NEW(SELECT_BUTTON))
        task->tKeyboardEvent = INPUT_SELECT;
    else if (JOY_NEW(START_BUTTON))
        task->tKeyboardEvent = INPUT_START;
    else if (JOY_NEW(R_BUTTON))
        task->tKeyboardEvent = INPUT_R_BUTTON;
    else if (JOY_NEW(L_BUTTON))
        task->tKeyboardEvent = INPUT_L_BUTTON;
    else
        HandleDpadMovement(task);
}

void TryTurnChineseRow(u8 y, u8 next)
{
    if (y == 0 && IsPageChMode())
    {
        TurnChinesePage(next);
    }
}

void HandleDpadMovement(struct Task *task)
    {
    s16 cursorX;
    s16 cursorY;

    GetCursorPos(&cursorX, &cursorY);
    if (JOY_REPEAT(DPAD_UP))
    {
        task->tKeyboardEvent = INPUT_DPAD_UP;
        cursorY--;
    }
    else if (JOY_REPEAT(DPAD_DOWN))
    {
        task->tKeyboardEvent = INPUT_DPAD_DOWN;
        cursorY++;
    }
    else if (JOY_REPEAT(DPAD_LEFT))
    {
        task->tKeyboardEvent = INPUT_DPAD_LEFT;
        cursorX--;
    }
    else if (JOY_REPEAT(DPAD_RIGHT))
    {
        task->tKeyboardEvent = INPUT_DPAD_RIGHT;
        cursorX++;
    }
    else
        return;
    if (cursorY < 0)
        cursorY = KBROW_COUNT - 1;
    if (cursorY > KBROW_COUNT - 1)
    {
        cursorY = !CanDPadMoveToTop();
    }
    else if (cursorY == 0 && !CanDPadMoveToTop())
    cursorY = KBROW_COUNT - 1;
// Wrap cursor position in the X direction
if (cursorX < 0)
{
    TryTurnChineseRow(cursorY, FALSE);
    cursorX = GetCurrentPageColumnCountByRow(cursorY) - 1;
}
else if (cursorX > GetCurrentPageColumnCountByRow(cursorY) - 1)
{
    cursorX = 0;
    TryTurnChineseRow(cursorY, TRUE);
}

SetCursorPos(cursorX, cursorY);
}

#undef tState
#undef tKeyboardEvent
#undef tButtonId

void (*const sDrawGenderIconFuncs[])(void) =
{
    [FALSE] = DummyGenderIcon,
    [TRUE]  = DrawGenderIcon,
};

void TryDrawGenderIcon(void)
{
    sDrawGenderIconFuncs[sNamingScreen->template->addGenderIcon]();
}

void DummyGenderIcon(void)
{

}

const u8 sGenderColors[2][3] = {
    [MALE]   = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_LIGHT_BLUE, TEXT_COLOR_BLUE},
    [FEMALE] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_LIGHT_RED, TEXT_COLOR_RED}
};

extern const u8 gText_MaleSymbol[];
extern const u8 gText_FemaleSymbol[];

void DrawGenderIcon(void)
{
    u8 genderSymbol[2];
    u8 text[2];
    bool8 gender = MALE;

    StringCopy(genderSymbol, gText_MaleSymbol);

    if (sNamingScreen->monGender != MON_GENDERLESS)
    {
        if (sNamingScreen->monGender == MON_FEMALE)
        {
            StringCopy(genderSymbol, gText_FemaleSymbol);
            gender = FEMALE;
        }
        AddTextPrinterParameterized3(sNamingScreen->windows[2], 2, 0x68, 1, sGenderColors[gender], 0xFF, genderSymbol);
    }
}

u8 GetTextEntryPosition(void)
{
    u8 i;

    for (i = 0; i < sNamingScreen->template->maxChars; i++)
    {
        if (sNamingScreen->textBuffer[i] == EOS)
            return i;
    }
    return sNamingScreen->template->maxChars - 1;
}

u16 GetCharAtKeyboardPos(s16 x, s16 y)
{
    if (y == 0)
    {
        const u8* chars = GetCurrentPageChChars();
        return (chars[x*2] << 8) | chars[x*2+1];
    }
	return gText_NamingScreenKeyboard_Words[CurrentPageToKeyboardId()][y - 1][x];
}

u8 GetPreviousTextCaretPosition(void)
{
    s8 i;

    for (i = sNamingScreen->template->maxChars - 1; i > 0; i--)
    {
        if (sNamingScreen->textBuffer[i] != EOS)
            return i;
    }
    return 0;
}

void DeleteTextCharacter(void)
{
	u8 keyRole = GetKeyRoleAtCursorPos();
	u8 index = GetPreviousTextCaretPosition();
    if (sNamingScreen->chBufferCount > 0)
    {
        sNamingScreen->chBuffer[--sNamingScreen->chBufferCount] = EOS;
        ReDrawChineseTextEntry();
        return;
    }
	
	if (2 == sNamingScreen->gTextMode[index])
	{
		sNamingScreen->textBuffer[index] = 0xFF;
		sNamingScreen->textBuffer[index - 1] = 0xFF;
		sNamingScreen->gTextMode[index] = 0;
		sNamingScreen->gTextMode[--index] = 0;
	}
	else
	{
		sNamingScreen->textBuffer[index] = 0xFF;
		sNamingScreen->gTextMode[index] = 0;
	}
	
	DrawTextEntry();
    if (keyRole == KEY_ROLE_CHAR || keyRole == KEY_ROLE_BACKSPACE)
        TryStartButtonFlash(BUTTON_BACK, FALSE, TRUE);
    PlaySE(23);
}

extern const u8 gText_EmptyString3[];
const u8* GetCurrentPageChChars()
{
    return &sNamingScreen->curChChars[sNamingScreen->chCharsPos * 2];
}

bool8 IsPageChMode()
{
    return sNamingScreen->currentPage == KBPAGE_LETTERS_CH;
}

bool8 CanDPadMoveToTop()
{
    return sNamingScreen->currentPage == KBPAGE_LETTERS_CH && sNamingScreen->chBufferCount > 0;
}

bool8 AddChCharacterBuffer(u16 ch)
{
    if (sNamingScreen->chBufferCount < sizeof (sNamingScreen->chBuffer)){
        sNamingScreen->chBuffer[sNamingScreen->chBufferCount++] = ch;
        sNamingScreen->chBuffer[sNamingScreen->chBufferCount] = EOS;
        return TRUE;
    }
    return FALSE;
}

// Ensure the complete definition of struct PingYinSerchNode is included

const u8* FindChChars()
{
    u16 i;
    for( i = 0; i < ARRAY_COUNT(gPingYinSerchNode); i++)
    {
        if (StringCompare(sNamingScreen->chBuffer, gPingYinSerchNode[i].key) == 0)
            return gPingYinSerchNode[i].val;
    }
    return gText_EmptyString3;
}

void CalcNextChars()
{
    u8 i;
    const u8* chars = GetCurrentPageChChars();
    for (i = 0;i < KBCOL_COUNT; i++)
    {
        if (chars[i * 2] == EOS)
            break;
    }
    sNamingScreen->curChRowMax = i;
}

void DrawChineseTextEntry()
{
    FillWindowPixelBuffer(sNamingScreen->windows[WIN_CH], 0);
    FillWindowPixelBuffer(sNamingScreen->windows[WIN_PINYIN], 0);
    StringCopyN(gStringVar1, GetCurrentPageChChars(), sNamingScreen->curChRowMax*2);
    gStringVar1[sNamingScreen->curChRowMax*2] = EOS;
    CopyStrClear(gStringVar1, gStringVar4, COLOUM_PAD - 7);
    AddTextPrinterParameterized3(sNamingScreen->windows[WIN_CH], 1, 4, 0, sKeyboardTextColors, 0, gStringVar4);
    AddTextPrinterParameterized3(sNamingScreen->windows[WIN_PINYIN], 0, 0, 0, sKeyboardTextColors, 0, sNamingScreen->chBuffer);
    PutWindowTilemap(sNamingScreen->windows[WIN_CH]);
    PutWindowTilemap(sNamingScreen->windows[WIN_PINYIN]);
    CopyBgTilemapBufferToVram(sWindowTemplates[WIN_CH].bg);
}

void ReDrawChineseTextEntry()
{
    sNamingScreen->curChChars = FindChChars();
    sNamingScreen->chCharsPos = 0;
    CalcNextChars();
    DrawChineseTextEntry();
}

void ClearChiniesTextEntry()
{
    sNamingScreen->chBufferCount = 0;
    sNamingScreen->chBuffer[0] = EOS;
    sNamingScreen->curChChars = gText_EmptyString3;
    sNamingScreen->chCharsPos = 0;
    DrawChineseTextEntry();
}


void TurnChinesePage(u8 next)
{
    if (next)
    {
        if (sNamingScreen->curChChars != NULL
            && GetCurrentPageChChars()[sNamingScreen->curChRowMax * 2] != EOS)
        {
            sNamingScreen->chCharsPos += sNamingScreen->curChRowMax;
        }
        else
            return;
    }
    else
    {
        if (sNamingScreen->chCharsPos > 0)
        {
            sNamingScreen->chCharsPos -= 12;
        }
        else
            return;
    }
    CalcNextChars();
    DrawChineseTextEntry();
    SetCursorPos(0, 0);
}


bool8 AddChCharacter(u16 ch)
{
    if (AddChCharacterBuffer(ch))
    {
        ReDrawChineseTextEntry();
        return TRUE;
    }
    return FALSE;
}

bool8 AddTextCharacter(void)
{
    s16 x;
    s16 y;
	u16 ch;
	u8 index;
	bool8 ismaxch;

    GetCursorPos(&x, &y);
    ch = GetCharAtKeyboardPos(x, y);
    if (y != 0 && IsPageChMode())
    {
        AddChCharacter(ch);
        return FALSE;
    }
    
	index = GetTextEntryPosition();
	if (ch >= 0x100) //是中文
	{
		if (GetPreviousTextCaretPosition() <= sNamingScreen->template->maxChars - 3)
		{
			sNamingScreen->textBuffer[index] = ch >> 8;
			sNamingScreen->textBuffer[index + 1] = ch & 0xFF;
			sNamingScreen->gTextMode[index] = 2;
			sNamingScreen->gTextMode[index + 1] = 2;
			ismaxch = FALSE;
            ClearChiniesTextEntry();
		}
		else ismaxch = TRUE;
	}
	else
	{
		if (GetPreviousTextCaretPosition() != sNamingScreen->template->maxChars - 1)
		{
			sNamingScreen->textBuffer[index] = ch;
			sNamingScreen->gTextMode[index] = 1;
			ismaxch = FALSE;
		}
		else ismaxch = TRUE;
	}
		
	DrawTextEntry();
    PlaySE(SE_SELECT);
	
	return ismaxch;
}

void SaveInputText(void)
{
    // Copy from the first non-whitespace character
    u8 i;

    for (i = 0; i < sNamingScreen->template->maxChars; i++)
    {
        if (sNamingScreen->textBuffer[i] != CHAR_SPACE && sNamingScreen->textBuffer[i] != EOS)
        {
            StringCopyN(sNamingScreen->destBuffer, sNamingScreen->textBuffer, sNamingScreen->template->maxChars + 1);
            break;
        }
    }
}

void LoadGfx(void)
{
    LZ77UnCompWram(gNamingScreenBg_Gfx, gDecompressionBuffer);
    LoadBgTiles(3, gDecompressionBuffer, 0x2000, 0);
    LZ77UnCompWram(gNamingScreenBg_Rect, sNamingScreen->rectBuffer);
    LZ77UnCompWram(gNamingScreenBg_Rect2, sNamingScreen->rectBuffer2);
    LoadSpriteSheets(sSpriteSheets);
    LoadSpritePalettes(sSpritePalettes);
}

void CreateHelperTasks(void)
{
    CreateInputHandlerTask();
    CreateButtonFlashTask();
}

void LoadPalettes(void)
{
    LoadPalette(gNamingScreenBg_Pal, 0, 0x40);
    LoadPalette(sKeyboard_Pal, 0xA0, sizeof(sKeyboard_Pal));
    LoadPalette(GetTextWindowPalette(2), 0xB0, 0x20);
}

void DecompressToBgTilemapBuffer(u8 bg, const u32 *src)
{
    CopyToBgTilemapBuffer(bg, src, 0, 0);
}

void DrawTextEntry(void)
{
	u8 temp[3];
    register u8 i;
	u8 maxChars = sNamingScreen->template->maxChars;
    u16 x = sNamingScreen->inputCharBaseXPos - 96;
	
	FillWindowPixelBuffer(sNamingScreen->windows[WIN_TEXT_ENTRY], PIXEL_FILL(0));
	for (i = 0; i < maxChars; i++)
    {
        if (2 == sNamingScreen->gTextMode[i])
		{
			temp[0] = sNamingScreen->textBuffer[i++];
			temp[1] = sNamingScreen->textBuffer[i];
			temp[2] = 0xFF;
            AddTextPrinterParameterized3(sNamingScreen->windows[WIN_TEXT_ENTRY], 1,  (i - 1) * 8 + x, 1, sKeyboardTextColors, 0xFF, temp);
		}
		else
		{
			temp[0] = sNamingScreen->textBuffer[i];
			temp[1] = 0xFF;
			temp[2] = 0xFF;
            AddTextPrinterParameterized3(sNamingScreen->windows[WIN_TEXT_ENTRY], 1,  i * 8 + x, 1, sKeyboardTextColors, 0xFF, temp);
		}
    }
	
	TryDrawGenderIcon();
    CopyWindowToVram(sNamingScreen->windows[WIN_TEXT_ENTRY], 2);
    CopyWindowToVram(sNamingScreen->windows[WIN_TEXT_ENTRY], COPYWIN_GFX);
    PutWindowTilemap(sNamingScreen->windows[WIN_TEXT_ENTRY]);
}

#define IS_CH_BEGIN(currChar) currChar >= 1 && currChar <= 0x1E
void CopyStrClear(const u8* src, u8* dest, u8 pad)
{
    u8 ch;
    *dest = ch = *src++;
    if (ch == EOS)
        return;
    dest++;
    if (IS_CH_BEGIN(ch))
        *dest++=*src++;
    while (*src != EOS)
    {
        *dest++ = EXT_CTRL_CODE_BEGIN;
        *dest++ = EXT_CTRL_CODE_CLEAR;
        *dest++ = pad;
        *dest++ = ch = *src++;
        if (IS_CH_BEGIN(ch))
            *dest++=*src++;
    }
    *dest = EOS;
}

void PrintKeyboardKeys(u8 window, u8 page)
{
    u16 i;
	u8 *buffer = gStringVar4;
    
    FillWindowPixelBuffer(window, 0);
    for (i = 0; i < KBROW_COUNT - 1; i++)
    {
        CopyStrClear(gText_NamingScreenKeyboard_Words[page][i], buffer, COLOUM_PAD);
        AddTextPrinterParameterized3(window, 1, 0, i * ROW_HEIGHT, sKeyboardTextColors, 0, buffer);
    }
    PutWindowTilemap(window);
}

void PrintControls(u8 page)
{
    const ALIGNED(1) u8 gText_Banner_Upper[] = _("拼音{CLEAR 7}ABC{CLEAR 8}1/?{CLEAR 23}删除文字{CLEAR 25}完毕");
    const ALIGNED(1) u8 gText_Banner_Lower[] = _("拼音{CLEAR 7}abc{CLEAR 8}1/?{CLEAR 23}删除文字{CLEAR 25}完毕");
    AddTextPrinterParameterized3(sNamingScreen->windows[WIN_BANNER], 0, 8, 6, &sKeyboardTextColors[3], 0,
        page == KBPAGE_LETTERS_UPPER ? gText_Banner_Upper : gText_Banner_Lower);
    PutWindowTilemap(sNamingScreen->windows[WIN_BANNER]);
    CopyWindowToVram(sNamingScreen->windows[WIN_BANNER], 3);
}

void CB2_NamingScreen(void)
{
    RunTasks();
    AnimateSprites();
    BuildOamBuffer();
    UpdatePaletteFade();
}

void ResetVHBlank(void)
{
    SetVBlankCallback(NULL);
    SetHBlankCallback(NULL);
}

void SetVBlank(void)
{
    SetVBlankCallback(VBlankCB_NamingScreen);
}

void VBlankCB_NamingScreen(void)
{
    LoadOam();
    ProcessSpriteCopyRequests();
    TransferPlttBuffer();
    SetGpuReg(REG_OFFSET_BG1VOFS, sNamingScreen->bg1vOffset);
    SetGpuReg(REG_OFFSET_BG2VOFS, sNamingScreen->bg2vOffset);
    SetGpuReg(REG_OFFSET_BG1CNT, GetGpuReg(REG_OFFSET_BG1CNT) & 0xFFFC); // clear priority bits
    SetGpuRegBits(REG_OFFSET_BG1CNT, sNamingScreen->bg1Priority);
    SetGpuReg(REG_OFFSET_BG2CNT, GetGpuReg(REG_OFFSET_BG2CNT) & 0xFFFC); // clear priority bits
    SetGpuRegBits(REG_OFFSET_BG2CNT, sNamingScreen->bg2Priority);
}

void NamingScreen_ShowBgs(void)
{
    ShowBg(0);
    ShowBg(1);
    ShowBg(2);
    ShowBg(3);
}

//--------------------------------------------------
// Forward-declared variables
//--------------------------------------------------

const struct NamingScreenTemplate sPlayerNamingScreenTemplate = {
    .copyExistingString = FALSE,
    .maxChars = PLAYER_NAME_LENGTH,
    .iconFunction = 1,
    .addGenderIcon = 0,
    .initialPage = KBPAGE_LETTERS_CH,
    .title = gText_YourName,
};

const struct NamingScreenTemplate sPcBoxNamingScreenTemplate = {
    .copyExistingString = FALSE,
    .maxChars = 8,
    .iconFunction = 2,
    .addGenderIcon = 0,
    .initialPage = KBPAGE_LETTERS_CH,
    .title = gText_BoxName,
};

const struct NamingScreenTemplate sMonNamingScreenTemplate = {
    .copyExistingString = FALSE,
    .maxChars = POKEMON_NAME_LENGTH,
    .iconFunction = 3,
    .addGenderIcon = 1,
    .initialPage = KBPAGE_LETTERS_CH,
    .title = gText_PkmnsNickname,
};

extern const u8 gText_RivalsName[];

const struct NamingScreenTemplate sRivalNamingScreenTemplate = {
    .copyExistingString = FALSE,
    .maxChars = PLAYER_NAME_LENGTH,
    .iconFunction = 4,
    .addGenderIcon = 0,
    .initialPage = KBPAGE_LETTERS_CH,
    .title = gText_RivalsName,
};

const struct NamingScreenTemplate *const sNamingScreenTemplates[] =
{
    [NAMING_SCREEN_PLAYER]     = &sPlayerNamingScreenTemplate,
    [NAMING_SCREEN_BOX]        = &sPcBoxNamingScreenTemplate,
    [NAMING_SCREEN_CAUGHT_MON] = &sMonNamingScreenTemplate,
    [NAMING_SCREEN_ENTER_SPECIES_NAME]   = &sMonNamingScreenTemplate,
    [NAMING_SCREEN_RIVAL]      = &sRivalNamingScreenTemplate,
};

const struct OamData sOam_8x8 = {
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .bpp = ST_OAM_4BPP,
    .shape = SPRITE_SHAPE(8x8),
    .x = 0,
    .size = SPRITE_SIZE(8x8),
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
};

const struct OamData sOam_16x16 = {
    .y = 0,
    .affineMode = ST_OAM_AFFINE_OFF,
    .objMode = ST_OAM_OBJ_NORMAL,
    .bpp = ST_OAM_4BPP,
    .shape = SPRITE_SHAPE(16x16),
    .x = 0,
    .size = SPRITE_SIZE(16x16),
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
};

const struct Subsprite sSubsprites_PCIcon[] = {
    {
        .x = -8,
        .y = -12,
        .shape = SPRITE_SHAPE(16x8),
        .size = SPRITE_SIZE(16x8),
        .tileOffset = 0,
        .priority = 3
    }, {
        .x = -8,
        .y = -4,
        .shape = SPRITE_SHAPE(16x8),
        .size = SPRITE_SIZE(16x8),
        .tileOffset = 2,
        .priority = 3
    }, {
        .x = -8,
        .y =  4,
        .shape = SPRITE_SHAPE(16x8),
        .size = SPRITE_SIZE(16x8),
        .tileOffset = 4,
        .priority = 3
    }
};

const struct SpriteFrameImage sImageTable_PCIcon[] = {
    {sPCIconOff_Gfx, sizeof(sPCIconOff_Gfx)},
    {sPCIconOn_Gfx, sizeof(sPCIconOn_Gfx)},
};

const union AnimCmd sAnim_Loop[] = {
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_JUMP(0)
};

const union AnimCmd sAnim_CursorSquish[] = {
    ANIMCMD_FRAME(4, 8),
    ANIMCMD_FRAME(8, 8),
    ANIMCMD_END
};

const union AnimCmd sAnim_PCIcon[] = {
    ANIMCMD_FRAME(0, 2),
    ANIMCMD_FRAME(1, 2),
    ANIMCMD_JUMP(0)
};

const union AnimCmd *const sAnims_Loop[] = {
    sAnim_Loop
};

const union AnimCmd *const sAnims_Cursor[] = {
    sAnim_Loop,
    sAnim_CursorSquish
};

const union AnimCmd *const sAnims_PCIcon[] = {
    sAnim_PCIcon
};

const u32 gNamingScreenPageSwapFrame_Gfx[]  = INCBIN_U32("graphics/naming_screen/page_swap_frame.4bpp");
const u32 gNamingScreenBackButton_Gfx[]     = INCBIN_U32("graphics/naming_screen/back_button.4bpp");
const u32 gNamingScreenOKButton_Gfx[]       = INCBIN_U32("graphics/naming_screen/ok_button.4bpp");
const u32 gNamingScreenPageSwapUpper_Gfx[]  = INCBIN_U32("graphics/naming_screen/page_swap_upper.4bpp");
const u32 gNamingScreenPageSwapLower_Gfx[]  = INCBIN_U32("graphics/naming_screen/page_swap_lower.4bpp");
const u32 gNamingScreenPageSwapOthers_Gfx[] = INCBIN_U32("graphics/naming_screen/page_swap_others.4bpp");
const u32 gNamingScreenCursor_Gfx[]         = INCBIN_U32("graphics/naming_screen/cursor.4bpp");
const u32 gNamingScreenCursorSquished_Gfx[] = INCBIN_U32("graphics/naming_screen/cursor_squished.4bpp");
const u32 gNamingScreenCursorFilled_Gfx[]   = INCBIN_U32("graphics/naming_screen/cursor_filled.4bpp");
const u32 gNamingScreenPageSwapButton_Gfx[] = INCBIN_U32("graphics/naming_screen/page_swap_button.4bpp");
const u32 gNamingScreenInputArrow_Gfx[]     = INCBIN_U32("graphics/naming_screen/input_arrow.4bpp");
const u32 gNamingScreenUnderscore_Gfx[]     = INCBIN_U32("graphics/naming_screen/underscore.4bpp");

const struct SpriteSheet sSpriteSheets[12] = {
    {gNamingScreenBackButton_Gfx,     0x1E0,  GFXTAG_BACK_BUTTON},
    {gNamingScreenOKButton_Gfx,       0x1E0,  GFXTAG_OK_BUTTON},
    {gNamingScreenPageSwapFrame_Gfx,  0x280,  GFXTAG_PAGE_SWAP_FRAME},
    {gNamingScreenPageSwapButton_Gfx, 0x100,  GFXTAG_PAGE_SWAP_BUTTON},
    {gNamingScreenPageSwapUpper_Gfx,  0x060,  GFXTAG_PAGE_SWAP_UPPER},
    {gNamingScreenPageSwapLower_Gfx,  0x060,  GFXTAG_PAGE_SWAP_LOWER},
    {gNamingScreenPageSwapOthers_Gfx, 0x060,  GFXTAG_PAGE_SWAP_OTHERS},
    {gNamingScreenCursor_Gfx,         0x080,  GFXTAG_CURSOR},
    {gNamingScreenCursorSquished_Gfx, 0x080,  GFXTAG_CURSOR_SQUISHED},
    {gNamingScreenCursorFilled_Gfx,   0x080,  GFXTAG_CURSOR_FILLED},
    {gNamingScreenInputArrow_Gfx,     0x020,  GFXTAG_INPUT_ARROW},
    {gNamingScreenUnderscore_Gfx,     0x020,  GFXTAG_UNDERSCORE}
};
const u16 gUnderscore_Pal[] =  INCBIN_U16("graphics/naming_screen/underscore.gbapal");

extern const u16 gNamingScreenMenu_Pal[6][16];

const struct SpritePalette sSpritePalettes[] = {
    {gNamingScreenMenu_Pal[0], PALTAG_PC_ICON},
    {gNamingScreenMenu_Pal[1], PALTAG_PAGE_SWAP_UPPER},
    {gNamingScreenMenu_Pal[2], PALTAG_PAGE_SWAP_LOWER},
    {gNamingScreenMenu_Pal[3], PALTAG_PAGE_SWAP_OTHERS},
    {gNamingScreenMenu_Pal[4], PALTAG_PAGE_SWAP},
    {gNamingScreenMenu_Pal[5], PALTAG_CURSOR},
    {gNamingScreenMenu_Pal[4], PALTAG_BACK_BUTTON},
    {gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON},
    {} // terminator
};
